#![feature(get_many_mut)]


use winit::{
    event::*,
    event_loop::{ControlFlow, EventLoop},
    keyboard::{KeyCode, PhysicalKey},
    window::WindowBuilder,
};
mod core;
mod render;

#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::js_sys;

use crate::render::Renderer;

#[cfg(target_arch = "wasm32")]
extern crate web_sys;

// A macro to provide `println!(..)`-style syntax for `console.log` logging.
#[cfg(target_arch = "wasm32")]
macro_rules! log {
    ( $( $t:tt )* ) => {
        web_sys::console::log_1(&format!( $( $t )* ).into());
    }
}
fn start_event_loop() {
    #[cfg(target_arch = "wasm32")]
    log!("===start_event_loop==");
    let event_loop = EventLoop::new().unwrap();
    let builder = WindowBuilder::new();

    #[cfg(target_arch = "wasm32")]
    let builder = {
        use winit::platform::web::WindowBuilderExtWebSys;
        builder.with_append(true)
    };
    #[cfg(target_arch = "wasm32")]
    log!("===crate window==");
    let window = builder.build(&event_loop).unwrap();
    #[cfg(target_arch = "wasm32")]
    {
        // Winit prevents sizing with CSS, so we have to set
        // the size manually when on web.
        use winit::dpi::PhysicalSize;
        use winit::platform::web::WindowExtWebSys;

        web_sys::window()
            .and_then(|win| win.document())
            .map(|doc| {
                match doc.get_element_by_id("wasm-example") {
                    Some(dst) => {
                        #[cfg(target_arch = "wasm32")]
                        log!("===crate canvas==");
                        window.set_min_inner_size(Some(PhysicalSize::new(450, 400)));
                        let canvas = window.canvas().expect("获取 canvas 失败");

                        let res = dst.append_child(&web_sys::Element::from(canvas));
                        match res {
                            Ok(_) => {
                                #[cfg(target_arch = "wasm32")]
                                log!("===Success==");
                            }
                            Err(e) => {
                                #[cfg(target_arch = "wasm32")]
                                log!("===获取 canvas 失败 {:?}==", e);
                            }
                        }
                    }
                    None => {
                        window.set_min_inner_size(Some(PhysicalSize::new(800, 800)));
                        #[cfg(target_arch = "wasm32")]
                        log!("===crate canvas2==");
                        let canvas = window.canvas().expect("获取 canvas1 失败");
                        canvas.style().set_css_text(
                            "background-color: black; display: block; margin: 20px auto;",
                        );
                        doc.body()
                            .map(|body| body.append_child(&web_sys::Element::from(canvas)));
                    }
                };
            })
            .expect("Couldn't append canvas to document body.");
    }
    #[cfg(target_arch = "wasm32")]
    log!("===Run==");

    let  renderer = Renderer::new(&window, true);
 
    let mut renderer = match renderer {
        Ok(renderer) => renderer,
        Err(e) => {
            #[cfg(target_arch = "wasm32")]
            log!("===获取 renderer 失败 {:?}==", e);
            panic!("===")
        }
    };
    let _ = event_loop.run(move |event, elwt| match event {
        Event::WindowEvent {
            ref event,
            window_id,
        } if window_id == window.id() => match event {
            WindowEvent::CloseRequested
            | WindowEvent::KeyboardInput {
                event:
                    KeyEvent {
                        physical_key: PhysicalKey::Code(KeyCode::Escape),
                        ..
                    },
                ..
            } => elwt.exit(),

            WindowEvent::RedrawRequested => {
                #[cfg(target_arch = "wasm32")]
                log!("===RedrawRequested==");
                let _ = renderer.draw();
            }
            _ => {}
        },
        Event::AboutToWait => {
            #[cfg(target_arch = "wasm32")]
            log!("===AboutToWait==");
            window.request_redraw();
        }
        _ => {}
    });
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
pub fn run() {
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "wasm32")] {
            std::panic::set_hook(Box::new(console_error_panic_hook::hook));
            console_log::init_with_level(log::Level::Warn).expect("Couldn't initialize logger");
        } else {
            env_logger::init();
        }
    }

    #[cfg(target_arch = "wasm32")]
    {
        wasm_bindgen_futures::spawn_local(async {
            let run_closure = Closure::once_into_js(start_event_loop);

            // 处理运行过程中抛出的 JS 异常。
            // 否则 wasm_bindgen_futures 队列将中断，且不再处理任何任务。
            if let Err(error) = call_catch(&run_closure) {
                let is_control_flow_exception =
                    error.dyn_ref::<js_sys::Error>().map_or(false, |e| {
                        e.message().includes("Using exceptions for control flow", 0)
                    });

                if !is_control_flow_exception {
                    web_sys::console::error_1(&error);
                }
            }

            #[wasm_bindgen]
            extern "C" {
                #[wasm_bindgen(catch, js_namespace = Function, js_name = "prototype.call.call")]
                fn call_catch(this: &JsValue) -> Result<(), JsValue>;
            }
        });
    }

    #[cfg(not(target_arch = "wasm32"))]
    start_event_loop();
}


